﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RemoteTestRunner.Contracts;
using System.Xml;
using System.IO;

namespace RemoteTestRunner.BusinessLogic
{
    internal class TrxParser2010 : TrxParserBase, ITrxParser
    {
        public TestResultSummary ParseTestResultsSummary(string testResultsPath)
        {

            var summary = new TestResultSummary();

            XmlNamespaceManager nsm;
            var doc = GetTestResultXmlDocument(testResultsPath, out nsm);

            // Check the XML file to ensure it is an actual test results file
            if (doc.SelectSingleNode("ns:TestRun", nsm) == null)
            {
                return null;
            }

            // Store the path
            summary.Path = testResultsPath;

            summary.TestRunName = doc.SelectSingleNode("ns:TestRun", nsm).Attributes["name"].Value;
            summary.TestStartTime =
                DateTime.Parse(doc.SelectSingleNode("ns:TestRun/ns:Times", nsm).Attributes["start"].Value);
            summary.TestRunId = doc.SelectSingleNode("ns:TestRun", nsm).Attributes["id"].Value;

            return summary;
        }

        public TestResult ParseTestResults(string testResultsPath)
        {
            var testResult = new TestResult();
            TestResultInfo testResultInfo;
            TestResultInfo dataDrivenResultInfo;
            XmlNamespaceManager nsm;
            var doc = GetTestResultXmlDocument(testResultsPath, out nsm);
            XmlNode unitTestNode;

            // Check the XML file to ensure it is an actual test results file
            if (doc.SelectSingleNode("ns:TestRun", nsm) == null)
            {
                return null;
            }

            // Store the path
            testResult.Path = testResultsPath;

            // Look for related XML files
            var multiAssertFiles =
                new List<string>(
                    Directory.GetFiles(Path.GetDirectoryName(testResult.Path), "*" + MultiAssertFileSuffix));
            multiAssertFiles.ForEach(file =>
            {
                var multiAssertResult = new TestResultMultiAssertResult
                {
                    FileName = file,
                    FileContents = File.ReadAllText(file)
                };

                testResult.MultiAssertResults.Add(multiAssertResult);
            });

            // Gather overall test statistics
            testResult.TestRunName = doc.SelectSingleNode("ns:TestRun", nsm).Attributes["name"].Value;
            testResult.TestStartTime =
                DateTime.Parse(doc.SelectSingleNode("ns:TestRun/ns:Times", nsm).Attributes["start"].Value);
            testResult.TestRunId = doc.SelectSingleNode("ns:TestRun", nsm).Attributes["id"].Value;
            testResult.Outcome = doc.SelectSingleNode("ns:TestRun/ns:ResultSummary", nsm).Attributes["outcome"].Value;

            testResult.TotalCount =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "total", "0"));
            testResult.TotalExecuted =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "executed", "0"));
            testResult.TotalPassed =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "passed", "0"));
            testResult.TotalFailed =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "failed", "0"));
            testResult.TotalTimedOut =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "timeout", "0"));
            testResult.TotalInconclusive =
                int.Parse(
                    Utilities.GetAttributeValue(
                        doc.SelectSingleNode("ns:TestRun/ns:ResultSummary/ns:Counters", nsm).Attributes, "inconclusive",
                        "0"));

            // Create a list of nodes that we'll be working with
            var unitTestResults = doc.SelectNodes("ns:TestRun/ns:Results/ns:UnitTestResult", nsm);

            if (unitTestResults != null)
            {
                // Iterate through each of the test results and gather the required information
                foreach (XmlNode unitTestResultNode in unitTestResults)
                {
                    testResultInfo = new TestResultInfo
                    {
                        TestId =
                            Utilities.GetAttributeValue(unitTestResultNode.Attributes, "testId"),
                        ExecutionId =
                            Utilities.GetAttributeValue(unitTestResultNode.Attributes,
                                                        "executionId"),
                        IsDataDriven =
                            (Utilities.GetAttributeValue(unitTestResultNode.Attributes,
                                                         "resultType") ==
                             "DataDrivenTest"),
                        Outcome =
                            Utilities.GetAttributeValue(unitTestResultNode.Attributes, "outcome"),
                        ResultType =
                            Utilities.GetAttributeValue(unitTestResultNode.Attributes, "resultType")
                    };

                    // default the message to the outcome.  If the test failed then the message will be updated with the failure message.
                    testResultInfo.Message = testResultInfo.Outcome;

                    // Gather method-level properties
                    unitTestNode =
                        doc.SelectSingleNode(
                            string.Format("ns:TestRun/ns:TestDefinitions/ns:UnitTest[@id='{0}']/ns:TestMethod",
                                          testResultInfo.TestId), nsm);

                    testResultInfo.MethodName = Utilities.GetAttributeValue(unitTestNode.Attributes, "name");
                    testResultInfo.ClassName = Utilities.GetAttributeValue(unitTestNode.Attributes, "className");

                    if (testResultInfo.IsDataDriven)
                    {
                        // The data-driven test results follow a slightly different format
                        var ddResults = unitTestResultNode.SelectNodes("ns:InnerResults/ns:UnitTestResult", nsm);

                        if (ddResults != null)
                        {
                            testResultInfo.Message = testResultInfo.Outcome == "Passed"
                                                         ? "[Data-Driven Test - all tests passed.]"
                                                         : "[Data-Driven Test - one or more tests failed.]";

                            foreach (XmlNode ddNode in ddResults)
                            {
                                // Process data-driven test results
                                dataDrivenResultInfo = new TestResultInfo
                                {
                                    TestId =
                                        Utilities.GetAttributeValue(ddNode.Attributes,
                                                                    "testId"),
                                    ExecutionId =
                                        Utilities.GetAttributeValue(ddNode.Attributes,
                                                                    "executionId"),
                                    Outcome =
                                        Utilities.GetAttributeValue(ddNode.Attributes,
                                                                    "outcome"),
                                    ResultType =
                                        Utilities.GetAttributeValue(ddNode.Attributes,
                                                                    "resultType"),
                                    DataRowInfo =
                                        int.Parse(
                                            Utilities.GetAttributeValue(ddNode.Attributes,
                                                                        "dataRowInfo")),
                                    IsDataDriven =
                                        (Utilities.GetAttributeValue(
                                            unitTestResultNode.Attributes, "resultType") ==
                                         "DataDrivenTest"),
                                    ClassName = testResultInfo.ClassName,
                                    MethodName = testResultInfo.MethodName
                                };

                                // If the test failed, gather failure details
                                var outcome = Utilities.GetAttributeValue(ddNode.Attributes, "outcome");

                                if (outcome == "Failed")
                                {
                                    dataDrivenResultInfo.Message =
                                        ddNode.SelectSingleNode("ns:Output/ns:ErrorInfo/ns:Message", nsm).InnerText;
                                    dataDrivenResultInfo.StackTrace =
                                        ddNode.SelectSingleNode("ns:Output/ns:ErrorInfo/ns:StackTrace", nsm).InnerText;
                                }

                                testResultInfo.DataDrivenResults.Add(dataDrivenResultInfo);
                            }
                        }
                    }
                    else
                    {
                        // Retrieve results for failed non-data-driven test
                        if (testResultInfo.Outcome == "Failed")
                        {
                            testResultInfo.Message =
                                unitTestResultNode.SelectSingleNode("ns:Output/ns:ErrorInfo/ns:Message", nsm).InnerText;
                            var stackTraceNode =
                                unitTestResultNode.SelectSingleNode("ns:Output/ns:ErrorInfo/ns:StackTrace", nsm);
                            testResultInfo.StackTrace = stackTraceNode != null ? stackTraceNode.InnerText : string.Empty;
                        }
                    }

                    testResult.TestResults.Add(testResultInfo);
                }
            }

            testResult.MultiAssertReport = ApplyXslTransformation(testResult);

            return testResult;
            
        }

        protected override string RootNamespace
        {
            get { return @"http://microsoft.com/schemas/VisualStudio/TeamTest/2010"; }
        }
    }
}
